/*******************************************************************************
 * Copyright (c) 2014 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution. 
 *
 * The Eclipse Public License is available at 
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 */
package com.itfitness.mqttlibrary;


import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;

import androidx.annotation.RequiresApi;

import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttPingSender;
import org.eclipse.paho.client.mqttv3.internal.ClientComms;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Default ping sender implementation on Android. It is based on AlarmManager.
 *
 * <p>This class implements the {@link MqttPingSender} pinger interface
 * allowing applications to send ping packet to server every keep alive interval.
 * </p>
 *
 * @see MqttPingSender
 */
class AlarmPingSender implements MqttPingSender {
	// Identifier for Intents, log messages, etc..
	private static final String TAG = "AlarmPingSender";

	// TODO: Add log.
	private ClientComms comms;
	private MqttService service;
	private volatile boolean hasStarted = false;

	ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(5);
	ScheduledFuture scheduledFuture;
	Runnable runnable;
	private WakeLock wakelock;
	private String wakeLockTag = MqttServiceConstants.PING_WAKELOCK;

	public AlarmPingSender(MqttService service) {
		if (service == null) {
			throw new IllegalArgumentException(
					"Neither service nor client can be null.");
		}
		this.service = service;
	}

	@Override
	public void init(ClientComms comms) {
		this.comms = comms;
		wakeLockTag = MqttServiceConstants.PING_WAKELOCK
				+ comms.getClient().getClientId();
		runnable = new Runnable() {
			@Override
			public void run() {

				PowerManager pm = (PowerManager) service
						.getSystemService(Service.POWER_SERVICE);
				wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
				wakelock.acquire(10*60*1000L /*10 minutes*/);

				// Assign new callback to token to execute code after PingResq
				// arrives. Get another wakelock even receiver already has one,
				// release it until ping response returns.
				IMqttToken token = comms.checkForActivity(new IMqttActionListener() {

					@Override
					public void onSuccess(IMqttToken asyncActionToken) {
						Log.d(TAG, "Success. Release lock(" + wakeLockTag + "):"
								+ System.currentTimeMillis());
						//Release wakelock when it is done.
						wakelock.release();
					}

					@Override
					public void onFailure(IMqttToken asyncActionToken,
										  Throwable exception) {
						Log.d(TAG, "Failure. Release lock(" + wakeLockTag + "):"
								+ System.currentTimeMillis());
						//Release wakelock when it is done.
						wakelock.release();
					}
				});

				if (token == null && wakelock.isHeld()) {
					wakelock.release();
				}
			}
		};
	}

	@Override
	public void start() {
		if (null!=runnable&&null!=scheduled&&null!=comms)
			scheduledFuture = scheduled.schedule(runnable, comms.getKeepAlive(), TimeUnit.MILLISECONDS);
		hasStarted = true;
	}

	@Override
	public void stop() {
		Log.d(TAG, "Unregister alarmreceiver to MqttService"+comms.getClient().getClientId());
		if(hasStarted){
			hasStarted = false;
//			scheduled.shutdown();
			scheduledFuture.cancel(true);
		}
	}

	@Override
	public void schedule(long delayInMilliseconds) {
		if (null!=runnable&&null!=scheduled&&null!=comms)
			scheduledFuture = scheduled.schedule(runnable, delayInMilliseconds, TimeUnit.MILLISECONDS);
	}

}
